import { LoadingOutlined, RightOutlined, InfoCircleFilled } from '@ant-design/icons'
import { useBoolean, useRefValue } from '@gnlab/utils/env-react'
import { Collapse } from 'antd'
import AutoSizer from 'react-virtualized-auto-sizer'
import { FixedSizeList as List } from 'react-window'
import InfiniteLoader from 'react-window-infinite-loader'
import {
  useComputedContacts,
  useExternalTransferNotifications,
  useShowCurrent,
  useContactItem,
  useKeepPositon,
  FilterMode,
} from '@/desktop/logics/contacts'
import useTabUnreadNotice from '@/desktop/logics/tab-unread-notice'
import { useStreams } from '@/lib/utils'
import { useContactType } from '@/logics/contacts'
import useUIMemory from '@/logics/memory'
import services from '@/services'
import { ContactType } from '@/services/contacts'
import './index.module.less'

import { Category } from '../category'
import ContactItem from '../contact-item'
import { Search } from '../search'
import WarnItems from '../warn-items'

export function Contacts() {
  useTabUnreadNotice()
  useExternalTransferNotifications()

  const [warnTotalCount] = useStreams(services.chat.metas.warnTotalCount)

  const applyFilterMode = useCallback((filterMode: FilterMode) => {
    // 对于客户，变为未读消息模式时，联系人列表由后端先过滤一次；其他联系人类型则目前是纯前端过滤（因为客户数量最大）
    const externalConditions = services.externals.conditions$.value
    if (externalConditions.unread !== (filterMode === FilterMode.Unread)) {
      services.externals.setConditions({ unread: filterMode === FilterMode.Unread })
    }
  }, [])
  const [filterMode, setFilterMode] = useUIMemory('contacts-filter-mode', FilterMode.All)
  useEffect(() => applyFilterMode(filterMode), [filterMode])

  const [showTransfers, setShowTransfers] = useUIMemory('show-transfer-externals', true)
  const [type, setType] = useContactType()
  const { unreads: typeUnreads, contacts: typeContactsData } = useComputedContacts(type, filterMode)

  const contactsData = (typeContactsData.externals ??
    typeContactsData.groups ??
    typeContactsData.internals)!
  const transfersData = typeContactsData.transfers

  const listRef = useRef<List | null>(null)
  useShowCurrent(setType, listRef.current, contactsData)

  const itemSize = 88
  const { Item, itemKey } = useContactItem(ContactItem)
  const { listOuterRef, onItemsRendered } = useKeepPositon(
    type,
    contactsData,
    itemSize,
    listRef.current,
  )

  const loaderProps = useMemo(() => {
    const source = services.contacts.types[type]
    const finish = source.fullLoaded
    return {
      isItemLoaded(index: number) {
        return finish || index < contactsData.length
      },
      itemCount: !finish ? contactsData.length + 1 : contactsData.length,
      // eslint-disable-next-line @typescript-eslint/promise-function-async
      loadMoreItems: () => (source.loading ? undefined : source.loadNext()),
    }
  }, [type, contactsData])

  const [loading] = useStreams(services.contacts.types[type].loading$)

  /*
  避免拉取数据后，实际显示的数据数量不变，导致永远不再触发 InfiniteLoader 的后续加载。

  问题原因：
  拉取联系人的接口拉的是全量数据，但是显示出来的是筛选过的（如按“未回复会话”筛选、按“只看他”企微筛选）。
  如果 InfiniteLoader 触发一次拉取后，拉到的刚好都是被筛掉的，那么显示出来的数量没变化，以后就不会再触发拉取了。

  解决办法：
  每当 allContacts 变化时（数量接近每次拉取的 pageSize），检查 contactsData 有没有变化，
  如果没有变化，说明此次拉取没有拉到新联系人，应该立刻再拉一次，直到全部拉取完或拉到新联系人。
  */
  const allContacts = services.contacts.types[type].list.value
  const contactsDataRef = useRefValue(contactsData)
  const prevAllContacts = useRef(allContacts)
  const prevContacts = useRef(contactsData)
  useEffect(() => {
    prevAllContacts.current = services.contacts.types[type].list.value
    prevContacts.current = contactsDataRef.current
  }, [type])
  useEffect(() => {
    let timeoutId: NodeJS.Timeout | undefined
    const source = services.contacts.types[type]
    if (allContacts.size - prevAllContacts.current.size >= source.pageSize / 2) {
      if (contactsDataRef.current.length <= prevContacts.current.length) {
        timeoutId = setTimeout(() => {
          if (!source.loading && !source.fullLoaded) void source.loadNext()
        }, 500)
      }
    }

    prevAllContacts.current = allContacts
    prevContacts.current = contactsDataRef.current

    return () => clearTimeout(timeoutId)
  }, [filterMode, type, allContacts])

  const [showingWarns, showWarns, hideWarns] = useBoolean(false) // 是否显示“预警会话列表“
  if (showingWarns) return <WarnItems onClose={hideWarns} />

  const renderList = ({ width, height }: { width: number; height: number }) => {
    return <InfiniteLoader {...loaderProps}>
      {({ onItemsRendered: loaderOnItemsrendered, ref: loaderRef }) => <List
        ref={value => {
          listRef.current = value
          loaderRef(value)
        }}
        onItemsRendered={(...args) => {
          onItemsRendered(...args)
          loaderOnItemsrendered(...args)
        }}
        key={type} // 通过指定 type 为 key，保证切换类型时，重置列表滚动条的位置
        height={height}
        width={width}
        itemCount={contactsData.length}
        itemSize={itemSize}
        itemData={contactsData}
        itemKey={itemKey}
        outerRef={listOuterRef}
      >
        {Item}
      </List>}
    </InfiniteLoader>
  }

  return <div styleName="contacts">
    <header>
      <Category
        type={ContactType.external}
        name="客户"
        isCurrent={type === ContactType.external}
        setType={setType}
        unread={typeUnreads.external}
        resetUnread={typeUnreads.reset}
      />
      <Category
        type={ContactType.group}
        name="群"
        isCurrent={type === ContactType.group}
        setType={setType}
        unread={typeUnreads.group}
        resetUnread={typeUnreads.reset}
      />
      <Category
        type={ContactType.internal}
        name="员工"
        isCurrent={type === ContactType.internal}
        setType={setType}
        unread={typeUnreads.internal}
        resetUnread={typeUnreads.reset}
      />
    </header>

    <Search type={type} filterMode={filterMode} setFilterMode={setFilterMode} />

    {warnTotalCount ? (
      <div styleName="warns-enter" onClick={showWarns}>
        <InfoCircleFilled styleName="info" />
        <span styleName="txt">敏感消息预警</span>
        <span styleName="count">{warnTotalCount}</span>
        <RightOutlined styleName="arrow" />
      </div>
    ) : null}

    <main>
      {transfersData ? (
        <>
          <Collapse
            styleName="transfer-wrap"
            defaultActiveKey={showTransfers ? ['1'] : []}
            onChange={(keys: string | string[]) =>
              setShowTransfers(typeof keys === 'string' ? keys === '1' : keys.includes('1'))
            }
            expandIconPosition="end"
            bordered={false}
          >
            <Collapse.Panel header={`转交客户（${transfersData.length}）`} key="1">
              {transfersData.map(item => <ContactItem
                key={item.contact.id}
                style={{}}
                {...item}
              />)}
            </Collapse.Panel>
          </Collapse>

          <div styleName="externals-header">我的客户</div>
        </>
      ) : null}

      <section styleName="contacts-wrap">
        <div css={s.contactsInner}>
          <AutoSizer>{renderList}</AutoSizer>
        </div>
        {loading ? <Loading /> : null}
      </section>
    </main>
  </div>
}

function Loading() {
  const [show, setShow] = useState(false)
  useEffect(() => setShow(true), [])
  return <div css={s.loading} className={show ? 'show' : ''}>
    <LoadingOutlined />
    加载中...
  </div>
}

const s = {
  contactsInner: css`
    height: 100%;
  `,
  loading: css`
    align-items: center;
    background-color: rgba(0, 153, 255, 0.7);
    bottom: 0;
    color: #fff;
    display: flex;
    height: 30px;
    justify-content: center;
    left: 0;
    opacity: 0;
    position: absolute;
    right: 0;
    transition: opacity 0.2s linear;
    &.show {
      opacity: 1;
    }
    .anticon {
      margin-right: 12px;
    }
  `,
}
